package top.lgd.service.person;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import top.lgd.base.LgdConstants;
import top.lgd.enums.AccountTypeEnum;
import top.lgd.mapper.person.BalanceMapper;
import top.lgd.pojo.entity.person.Account;
import top.lgd.pojo.entity.person.Balance;
import top.lgd.pojo.entity.person.Salary;
import top.lgd.pojo.res.person.BalanceLine;
import top.lgd.pojo.res.person.PersonOverviewRes;
import top.lgd.service.LgdCacheService;
import top.lgd.utils.CollectionUtils;
import top.lgd.utils.DateUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author YYJ
 * @version 1.0
 */
@Service
public class BalanceService extends ServiceImpl<BalanceMapper, Balance> implements IService<Balance> {

    @Autowired
    private AccountService accountService;
    @Autowired
    private SalaryService salaryService;
    @Autowired
    private LgdCacheService lgdCacheService;

    public PersonOverviewRes queryOverview(Long dataUserId) {
        final String key = LgdConstants.KEY_PERSON_OVERVIEW_INFO + dataUserId;

        PersonOverviewRes personOverviewRes = lgdCacheService.get(key);
        if (null != personOverviewRes) {
            return personOverviewRes;
        }

        personOverviewRes = new PersonOverviewRes();
        List<Balance> balanceList = list(new QueryWrapper<Balance>().eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .eq(LgdConstants.COLUMN_STATUS, 1)
                .orderByDesc(LgdConstants.COLUMN_BALANCE_MONTH)
                .last(LgdConstants.SQL_LIMIT + 3));
        Balance balance = null;
        Balance lastBalance = null;
        Balance beforeBalance = null;
        if (CollectionUtils.isNotEmpty(balanceList)) {
            if (balanceList.size() > 0) {
                balance = balanceList.get(0);
            }
            if (balanceList.size() > 1) {
                lastBalance = balanceList.get(1);

            }
            if (balanceList.size() > 2) {
                beforeBalance = balanceList.get(2);
            }
        }

        if (null != balance) {
            personOverviewRes.setAssetSum(balance.getAssetSum());
            personOverviewRes.setDebtSum(balance.getDebtSum());
            personOverviewRes.setNetAssetSum(balance.getNetAssetSum());
            if (null != lastBalance) {
                personOverviewRes.setLastAssetSum(lastBalance.getAssetSum());
                personOverviewRes.setLastDebtSum(lastBalance.getDebtSum());
                personOverviewRes.setLastNetAssetSum(lastBalance.getNetAssetSum());
                if (null != beforeBalance) {
                    personOverviewRes.setLastNetAssetSub(lastBalance.getNetAssetSum().subtract(beforeBalance.getNetAssetSum()));
                    personOverviewRes.setLastNetAssetRatio(personOverviewRes.getLastNetAssetSub().divide(beforeBalance.getNetAssetSum(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)) + "%");
                } else {
                    personOverviewRes.setLastNetAssetSub(BigDecimal.ZERO);
                    personOverviewRes.setLastNetAssetRatio("0.00%");
                }
                personOverviewRes.setNetAssetSub(balance.getNetAssetSum().subtract(lastBalance.getNetAssetSum()));
                personOverviewRes.setNetAssetRatio(personOverviewRes.getNetAssetSub().divide(lastBalance.getNetAssetSum(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)) + "%");
            } else {
                personOverviewRes.setLastAssetSum(BigDecimal.ZERO);
                personOverviewRes.setLastDebtSum(BigDecimal.ZERO);
                personOverviewRes.setLastNetAssetSum(BigDecimal.ZERO);
                personOverviewRes.setLastNetAssetSub(BigDecimal.ZERO);
                personOverviewRes.setLastNetAssetRatio("0.00%");
                personOverviewRes.setNetAssetSub(BigDecimal.ZERO);
                personOverviewRes.setNetAssetRatio("0.00%");
            }
            personOverviewRes.setNetAssetTrend(personOverviewRes.getNetAssetSub().compareTo(BigDecimal.ZERO) > 0 ? LgdConstants.UP : LgdConstants.DOWN);
            personOverviewRes.setLastNetAssetTrend(personOverviewRes.getLastNetAssetSub().compareTo(BigDecimal.ZERO) > 0 ? LgdConstants.UP : LgdConstants.DOWN);
        }

        LocalDate toDay = LocalDate.now();
        String month = DateUtils.getMonth(toDay);
        String beforeMonth = DateUtils.getMonth(toDay.minus(1, ChronoUnit.YEARS));
        String lastMonth = DateUtils.getMonth(toDay.minus(1, ChronoUnit.MONTHS));
        String beforeLastMonth = DateUtils.getMonth(toDay.minus(1, ChronoUnit.MONTHS).minus(1, ChronoUnit.YEARS));

        Set<String> monthSet = new HashSet<>();
        monthSet.add(month);
        monthSet.add(beforeMonth);
        monthSet.add(lastMonth);
        monthSet.add(beforeLastMonth);

        List<Salary> salaryList = salaryService.list(new QueryWrapper<Salary>().eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .in(LgdConstants.COLUMN_SALARY_MONTH, monthSet));

        Map<String, Salary> salaryMap = salaryList.stream().collect(Collectors.toMap(Salary::getSalaryMonth, salary -> salary));

        Salary salary = salaryMap.get(month);
        if (null != salary) {
            personOverviewRes.setInAmount(salary.getAfterTax());
            Salary beforeSalary = salaryMap.get(beforeMonth);
            if (null != beforeSalary) {
                personOverviewRes.setInAmountSub(salary.getAfterTax().subtract(beforeSalary.getAfterTax()));
                personOverviewRes.setInAmountRatio(personOverviewRes.getInAmountSub().divide(beforeSalary.getAfterTax(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)) + "%");
            } else {
                personOverviewRes.setInAmountSub(BigDecimal.ZERO);
                personOverviewRes.setInAmountRatio("0.00%");
            }
            personOverviewRes.setInAmountTrend(personOverviewRes.getInAmountSub().compareTo(BigDecimal.ZERO) > 0 ? LgdConstants.UP : LgdConstants.DOWN);
        }

        Salary lastSalary = salaryMap.get(lastMonth);
        if (null != lastSalary) {
            personOverviewRes.setLastInAmount(lastSalary.getAfterTax());
            Salary beforeLastSalary = salaryMap.get(beforeLastMonth);
            if (null != beforeLastSalary) {
                personOverviewRes.setLastInAmountSub(lastSalary.getAfterTax().subtract(beforeLastSalary.getAfterTax()));
                personOverviewRes.setLastInAmountRatio(personOverviewRes.getInAmountSub().divide(beforeLastSalary.getAfterTax(), 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)) + "%");

            } else {
                personOverviewRes.setLastInAmountSub(BigDecimal.ZERO);
                personOverviewRes.setLastInAmountRatio("0.00%");
            }
            personOverviewRes.setLastInAmountTrend(personOverviewRes.getLastInAmountSub().compareTo(BigDecimal.ZERO) > 0 ? LgdConstants.UP : LgdConstants.DOWN);
        }

        lgdCacheService.put(key, personOverviewRes);
        return personOverviewRes;
    }

    public List<BalanceLine> queryBalanceList(Long dataUserId) {
        final String key = LgdConstants.KEY_PERSON_BALANCE_LIST + dataUserId;

        List<BalanceLine> balanceLineList = lgdCacheService.get(key);
        if (CollectionUtils.isNotEmpty(balanceLineList)) {
            return balanceLineList;
        }

        List<Balance> balanceList = list(new QueryWrapper<Balance>()
                .eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .orderByDesc(LgdConstants.COLUMN_BALANCE_MONTH)
                .last(LgdConstants.SQL_LIMIT + 30));

        balanceLineList = new ArrayList<>();
        for (Balance b : balanceList) {
            balanceLineList.add(new BalanceLine(b.getBalanceMonth(), "总资产", b.getAssetSum()));
            balanceLineList.add(new BalanceLine(b.getBalanceMonth(), "总负债", b.getDebtSum()));
            balanceLineList.add(new BalanceLine(b.getBalanceMonth(), "净资产", b.getNetAssetSum()));
            balanceLineList.add(new BalanceLine(b.getBalanceMonth(), "净增长", b.getNetGrowthAmount()));
        }

        lgdCacheService.put(key, balanceLineList);
        return balanceLineList;
    }

    public boolean staticBalance(Long dataUserId) {
        List<Account> accountList = accountService.list(new QueryWrapper<Account>().eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .eq(LgdConstants.COLUMN_STATUS, 1).orderByAsc(LgdConstants.COLUMN_ID));

        Map<String, BigDecimal> accountSumMap = accountList.stream()
                .collect(Collectors.groupingBy(
                        Account::getAccountType,
                        Collectors.reducing(BigDecimal.ZERO, Account::getBalance, BigDecimal::add)
                ));

        Balance balance = new Balance();
        balance.setDataUserId(dataUserId);
        balance.setBalanceMonth(DateUtils.getMonth());
        balance.setFinanceAmount(accountSumMap.getOrDefault(AccountTypeEnum.finance.name(), BigDecimal.ZERO));
        balance.setClaimAmount(accountSumMap.getOrDefault(AccountTypeEnum.claim.name(), BigDecimal.ZERO));
        balance.setCreditAmount(accountSumMap.getOrDefault(AccountTypeEnum.credit.name(), BigDecimal.ZERO));
        balance.setDebtAmount(accountSumMap.getOrDefault(AccountTypeEnum.debt.name(), BigDecimal.ZERO));
        balance.setLoanAmount(accountSumMap.getOrDefault(AccountTypeEnum.loan.name(), BigDecimal.ZERO));

        balance.setAssetSum(balance.getFinanceAmount().add(balance.getClaimAmount()));
        balance.setDebtSum(balance.getCreditAmount().add(balance.getDebtAmount()).add(balance.getLoanAmount()));
        balance.setNetAssetSum(balance.getAssetSum().subtract(balance.getDebtSum()));

        Balance lastBalance = getOne(new QueryWrapper<Balance>()
                .eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .eq(LgdConstants.COLUMN_BALANCE_MONTH, DateUtils.getLastMonth()));
        if (null != lastBalance) {
            balance.setNetGrowthAmount(balance.getNetAssetSum().subtract(lastBalance.getNetAssetSum()));
        }

        boolean result = saveOrUpdate(balance, new QueryWrapper<Balance>()
                .eq(LgdConstants.COLUMN_DATA_USER_ID, dataUserId)
                .eq(LgdConstants.COLUMN_BALANCE_MONTH, balance.getBalanceMonth()));

        clearCache(dataUserId);
        return result;
    }

    private void clearCache(Long dataUserId) {
        lgdCacheService.delete(LgdConstants.KEY_PERSON_OVERVIEW_INFO + dataUserId);
        lgdCacheService.delete(LgdConstants.KEY_PERSON_BALANCE_LIST + dataUserId);
    }
}